﻿using System;

using OCXS;
using Conf = OCXS.OCXSServiceLibrary.Configuration;
using OCXS.OCXSServiceLibrary.OPCXMLDA10;

namespace OCXS {

	namespace OCXSServiceLibrary {
        
		public partial class OCXSService {

			/// <summary>
            /// <para>
            /// Implements OPC XML DA 1.0 Read service. 
            /// </para>
            /// <para>
            /// See OPC XML DA 1.0 specification for more information about Read service.
            /// </para>
			/// </summary>
            /// <remarks>
            /// <para>
            ///     <b>Following there's a list of implemented (or not) standard features.</b>
            /// </para>
            /// <para>
            ///     The following <c>ReadRequest</c> standard features are (or not) implemented.
            /// 
            ///     <ul>
            ///         <li>
            ///             <b>ItemList: </b> the following features are (or not) supported:
            ///             <ul>
            ///                 <li><b>ItemPath: </b> ignored. Specific per-Item path and name are considered.</li>
            ///                 <li>
            ///                     <b>Items: </b> the following features are (or not) supported:
            ///                     <ul>
            ///                         <li><b>ClientItemHandle: </b> supported.</li>
            ///                         <li><b>ItemName: </b> supported.</li>
            ///                         <li><b>ItemPath: </b> supported.</li>
            ///                         <li><b>MaxAge: </b> not supported.</li>
            ///                         <li><b>MaxAgeSpecified: </b> ignored.</li>
            ///                         <li><b>ReqType: </b> not supported.</li>
            ///                     </ul>
            ///                 </li>
            ///                 <li><b>MaxAge: </b> not supported.</li>
            ///                 <li><b>MaxAgeSpecified: </b> ignored.</li>
            ///                 <li><b>ReqType: </b> not supported.</li>
            ///             </ul>
            ///         </li>
            ///         
            ///         <li>
            ///             <b>Options: </b> the following features are (or not) supported:
            ///             <ul>
            ///                 <li><b>ClientRequestHandle: </b> supported.</li>
            ///                 <li><b>LocaleID: </b> supported.</li>
            ///                 <li><b>RequestDeadline: </b> not supported.</li>
            ///                 <li><b>RequestDeadlineSpecified: </b> ignored.</li>
            ///                 <li><b>ReturnDiagnosticInfo: </b> supported.</li>
            ///                 <li><b>ReturnErrorText: </b> supported.</li>
            ///                 <li><b>ReturnItemName: </b> supported.</li>
            ///                 <li><b>ReturnItemPath: </b> supported.</li>
            ///                 <li><b>ReturnItemTime: </b> supported.</li>
            ///             </ul>
            ///         </li>
            ///         
            ///     </ul>
            /// </para>
            /// <para>
            ///     The following <c>ReadResponse</c> standard features are (or not) implemented.
            ///     <ul>
            ///         
            ///         <li>
            ///             <b>Errors: </b> One error per time is reported. The following errors are supported:
            ///             <ul>
            ///                 <li><c>E_OUTOFMEMORY</c></li>
            ///                 <li><c>E_FAIL</c></li>
            ///             </ul>
            ///         </li>
            ///         
            ///         <li>
            ///             <b>ReadResult: </b> the following features are (or not) supported:
            ///             <ul>
            ///                 <li><b>ClientRequestHandle: </b> supported.</li>
            ///                 <li><b>RcvTime: </b> supported (evaluated as first instruction).</li>
            ///                 <li><b>ReplyTime: </b> supported (evaluated as last instruction).</li>
            ///                 <li><b>RevisedLocaleID: </b> supported (<c>en-us</c>).</li>
            ///                 <li><b>ServerState: </b> supported (<c>running</c>).</li>
            ///             </ul>
            ///         </li>
            ///         
            ///         <li>
            ///             <b>RItemList: </b> the following features are (or not) supported:
            ///             <ul>
            ///                 <li>
            ///                     <b>ItemList: </b> the following features are (or not) supported:
            ///                     <ul>
            ///                         <li>
            ///                             <b>Items: </b> the following features are (or not) supported:
            ///                             <ul>
            ///                                 <li><b>ClientItemHandle: </b> supported.</li>
            ///                                 <li><b>DiagnosticInfo: </b> supported.</li>
            ///                                 <li><b>ItemName: </b> supported.</li>
            ///                                 <li><b>ItemPath: </b> supported.</li>
            ///                                 <li><b>Quality: </b> supported (static values: <c>good</c>).</li>
            ///                                 <li><b>ResultID: </b> supported. Only <c>E_UNKNOWNITEMNAME</c> error is supported.</li>
            ///                                 <li><b>Timestamp: </b> supported.</li>
            ///                                 <li><b>TimestampSpecified: </b> supported</li>
            ///                                 <li><b>Value: </b> supported.</li>
            ///                                 <li><b>ValueTypeQualifier: </b> supported (<c>xs:float</c>).</li>
            ///                             </ul>
            ///                         </li>
            ///                     </ul>
            ///                 </li>
            ///             </ul>
            ///         </li>
            ///         
            ///     </ul>
            /// </para>
            /// 
            /// <code>
            /// // The following code can be found in OCXSClient and 
            /// // shows how to call GetStatus service.
            /// class Program {
            ///
            ///     private const string EndpointAddress = "http://localhost/OCXS/OCXS.svc";
            ///     
            ///     static void Main(string[] args) {
            ///     
            ///     // The following get properties of default defined
            ///     // Items in Read().
            ///     Read("en-us", "0x00FF");
            /// }
            ///     
            /// public static ReadResponse Read(
            /// 	string localeID,
            /// 	string clientRequestHandle,
            /// 	bool returnItemName = true,
            /// 	bool returnItemPath = true,
            /// 	bool returnItemTime = true,
            /// 	bool returnDiagnosticInfo = false,
            /// 	bool returnErrorText = false,
            /// 	ReadRequestItemList requestItemList = null,
            /// 	// Not supported features down here
            /// 	bool requestDeadlineSpecified = false)
            /// {
            /// 	// Get the channel.
            /// 	// GetOCXSChannel can be found in OCXSClient.
            /// 	IOCXSService svc = GetOCXSChannel(EndpointAddress);
            /// 
            /// 	// Create request options, for ReadRequest.
            /// 	RequestOptions ro = new RequestOptions();
            /// 	ro.ClientRequestHandle = clientRequestHandle;
            /// 	ro.LocaleID = localeID;
            /// 	ro.RequestDeadline = DateTime.Parse("2001-09-11");
            /// 	ro.RequestDeadlineSpecified = requestDeadlineSpecified;
            /// 	ro.ReturnDiagnosticInfo = returnDiagnosticInfo;
            /// 	ro.ReturnErrorText = returnErrorText;
            /// 	ro.ReturnItemName = returnItemName;
            /// 	ro.ReturnItemPath = returnItemPath;
            /// 	ro.ReturnItemTime = returnItemTime;
            /// 
            /// 	// Let's create a valid ReadRequestItemList if not provided.
            /// 	// This way is shown how to create a valid ReadRequestItemList.
            /// 	ReadRequestItemList requestItems = requestItemList;
            /// 	if (requestItems == null) {
            /// 		requestItems = new ReadRequestItemList();
            /// 
            /// 		// requestItems.Items is a ReadRequestItem[].
            /// 		// There are two 'main ways' to obtain an ReadRequestItem[]:
            /// 		//   1. Creating a static fixed size array.
            /// 		//
            /// 		//   2. Create a List&lt;ReadRequestItem&gt; and call ToArray() method
            /// 		//      when finished adding ReadRequestItems.
            /// 		//
            /// 		// The solution (1) follows.
            /// 		requestItems.Items = new ReadRequestItem[2];
            /// 
            /// 		requestItems.Items[0] = new ReadRequestItem();
            /// 		requestItems.Items[0].ClientItemHandle = clientRequestHandle;
            /// 		requestItems.Items[0].ItemName = "MyVar1"; // valid name in default config
            /// 		requestItems.Items[0].ItemPath = "Root/Fold1/";
            /// 		// The following parameters are not supported.
            /// 		requestItems.Items[0].MaxAge = 0;
            /// 		requestItems.Items[0].MaxAgeSpecified = false;
            /// 		requestItems.Items[0].ReqType = null;
            /// 
            /// 		requestItems.Items[1] = new ReadRequestItem();
            /// 		requestItems.Items[1].ClientItemHandle = clientRequestHandle;
            /// 		requestItems.Items[1].ItemName = "MyVar3"; // invalid name in default config
            /// 		requestItems.Items[1].ItemPath = "Root/Fold1/";
            /// 		// The following parameters are not supported.
            /// 		requestItems.Items[1].MaxAge = 0;
            /// 		requestItems.Items[1].MaxAgeSpecified = false;
            /// 		requestItems.Items[1].ReqType = null;
            /// 
            /// 		// The following parameters are not supported.
            /// 		requestItems.ItemPath = null;
            /// 		requestItems.MaxAge = 0;
            /// 		requestItems.MaxAgeSpecified = false;
            /// 		requestItems.ReqType = null;
            /// 	}
            /// 
            /// 	// Create the request. Mind that formal parameters with default 
            /// 	// values usually correspond to not supported features.
            /// 	ReadRequest RR = new ReadRequest(ro, requestItems);
            ///     
            ///     // Perform the call to service and get the response.
            /// 	ReadResponse res = svc.Read(RR);
            /// 
            ///     // Print the response.
            ///     // PrintResponse can be found in OCXSClient.
            /// 	PrintResponse(res);
            /// 
            /// 	return res;
            /// }
            /// </code>
            /// 
            /// </remarks>
            /// <param name="request">The <c>ReadRequest</c> issued by client according to OCP XML DA 1.0 specification.</param>
            /// <returns>A <c>ReadResponse</c> according to OPC XML DA 1.0 specification.</returns>
			public ReadResponse Read(ReadRequest request) {
                
                ReadResponse RR = null;
                // Get the reply base
                ReplyBase RB = this.GetReplyBase(request.Options.ClientRequestHandle, request.Options.LocaleID);

                try {
                    // Foreach element requested need to get the corresponding ItemValue
                    ItemValue[] items = new ItemValue[request.ItemList.Items.Length];

                    for (int i = 0; i < request.ItemList.Items.Length; i++) {
                        ItemValue iv = this.GetItemValueByReadRequestItem(
                                            request.ItemList.Items[i],
                                            request.Options.ReturnItemName,
                                            request.Options.ReturnItemPath,
                                            request.Options.ReturnItemTime,
                                            request.Options.ReturnDiagnosticInfo);
                        items[i] = iv;
                    }

                    ReplyItemList RIL = new ReplyItemList();
                    RIL.Items = items;
                    RIL.Reserved = null;

                    RR = new ReadResponse(
                            RB, 
                            RIL, 
                            null); // OPCError

                    RB.ReplyTime = DateTime.Now;

                    return RR;

                } catch (OutOfMemoryException e) {

                    RB.ReplyTime = DateTime.Now;

                    // Return proper out of memory error message.
                    return new ReadResponse(
                            RB,
                            null,
                            new OPCError[] { this.BuildOPCError("E_OUTOFMEMORY", e.Message) });

                } catch (Exception e) {

                    RB.ReplyTime = DateTime.Now;

                    // Return proper out of memory error message.
                    return new ReadResponse(
                            RB,
                            null,
                            new OPCError[] { this.BuildOPCError("E_FAIL", e.Message) });
                }

			}

            /// <summary>
            /// Performs read operation in the variable pool according to provided parameters specified
            /// in the request.
            /// </summary>
            /// <param name="rri">The read value, also containing variable identifier.</param>
            /// <param name="retName">If <c>true</c> the ItemName will be returned in the read response.</param>
            /// <param name="retPath">If <c>true</c> the ItemPath will be returned in the read response.</param>
            /// <param name="retTime">If <c>true</c> the Timestamp will be returned in the read response.</param>
            /// <param name="retDiag">If <c>true</c> the DiagnosticInfo will be returned in the read response.</param>
            /// <returns>The read <c>ItemValue</c>.</returns>
            private ItemValue GetItemValueByReadRequestItem(
                                    ReadRequestItem rri, 
                                    bool retName = false, 
                                    bool retPath = false, 
                                    bool retTime = false,
                                    bool retDiag = false) {

                // Get requested variable and generate ItemValue
                Conf.VariableType v = this.GetVariableById(rri.ItemPath, rri.ItemName);
                
                ItemValue iv = new ItemValue();
                iv.DiagnosticInfo = (retDiag) ? "Diagnostics ok! Hurray!" : null;
                iv.ItemPath = (retPath && (v != null)) ? v.VariablePath : null;
                iv.ItemName = (retName && (v != null)) ? v.VariableName : null;
                iv.ClientItemHandle = rri.ClientItemHandle;

                iv.ResultID = (v == null)
                                ? new System.Xml.XmlQualifiedName("E_UNKNOWNITEMNAME", "http://opcfoundation.org/webservices/XMLDA/1.0/") 
                                : null;
                
                iv.ValueTypeQualifier = new System.Xml.XmlQualifiedName("float", "xs");
                iv.Value = (v == null) ? null : v.VariableValue;
                iv.Timestamp = (retTime) ? DateTime.Now : DateTime.MinValue;
                iv.TimestampSpecified = retTime;
                iv.Quality = new OPCQuality();
                iv.Quality.QualityField = qualityBits.good;
                iv.Quality.LimitField = limitBits.none;
                iv.Quality.VendorField = 0;

                return iv;

            }

		} /* OCXService */

	}

}
